package com.hzncc.flowable_diboot.contract.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.diboot.core.config.BaseConfig;
import com.diboot.core.util.MapUtils;
import com.diboot.core.util.S;
import com.diboot.core.vo.FileVO;
import com.diboot.file.entity.FileRecord;
import com.diboot.file.service.FileRecordService;
import com.diboot.file.service.FileStorageService;
import com.diboot.file.util.FileHelper;
import com.diboot.iam.entity.IamUser;
import com.diboot.iam.service.IamUserService;
import com.diboot.iam.util.IamSecurityUtils;
import com.hzncc.flowable_diboot.contract.entity.ContractProduct;
import com.hzncc.flowable_diboot.contract.entity.OaTemplate;
import com.hzncc.flowable_diboot.contract.service.ContractAuditService;
import com.hzncc.flowable_diboot.contract.service.FieldMappingService;
import com.hzncc.flowable_diboot.contract.service.OaTemplateService;
import com.hzncc.flowable_diboot.contract.vo.*;
import com.hzncc.flowable_diboot.enums.ContractAuditTypeEnum;
import com.hzncc.flowable_diboot.interfacelog.annotation.InterfaceLog;
import com.hzncc.flowable_diboot.project_contracts.vo.ProjectContractAuditDetailVO;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import jakarta.annotation.Resource;

import java.io.File;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Log4j2
public class OaInterfaceService {
    @Resource
    private ContractAuditService contractAuditService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private FieldMappingService fieldMappingService;

    @Autowired
    private FileRecordService fileRecordService;

    @Autowired
    private FileStorageService fileStorageService;

    @Autowired
    private IamUserService iamUserService;

    @Value("${oa.authUsername}")
    private String oaAuthUsername;

    @Value("${oa.authPassword}")
    private String oaAuthPassword;

    @Value("${oa.url}")
    private String oaUrl;

    @Autowired
    private OaTemplateService oaTemplateService;

    /**
     * 根据合同ID获取请求参数
     *
     * @param contractId 合同ID
     * @return 包含请求参数的MultiValueMap
     */
    public MultiValueMap<String, Object> getRequestParam(String contractId) {
        // 根据合同ID获取合同详情
        ContractAuditVO contractDetail = contractAuditService.getContractDetail(contractId);
        // 添加额外参数
        addExtParam(contractDetail);
        // 请求封装
        MultiValueMap<String, Object> oaReqVO;
        try {
            // 如果业务模型不是IPL
            if (!S.equals(contractDetail.getBusinessModel(), "IPL")) {
                // 遍历合同产品列表
                for (ContractProductListVO productListVO : contractDetail.getContractProductVOList()) {
                    // 计算销售目录价
                    productListVO.setListPrice(productListVO.getContractProductMaterialVOList()
                            // 计算每个产品的总价
                            .stream().map(e -> (e.getMaterialPrice() == null ? BigDecimal.ZERO : e.getMaterialPrice()).multiply(new BigDecimal(e.getMaterialCount() == null ? 0 : e.getMaterialCount())))
                            // 累加所有产品的总价
                            .reduce(BigDecimal.ZERO, BigDecimal::add).toString());
                }
            }
            // 获取OA请求参数
            oaReqVO = getOaReqVO(contractDetail);
        } catch (Exception e) {
            // 抛出运行时异常
            throw new RuntimeException(e);
        }
        return oaReqVO;
    }

    /**
     * 添加额外参数
     *
     * @param contractDetail
     * @author 陈广煜
     * @date 2025-02-14
     */
    private void addExtParam(ContractAuditVO contractDetail) {
        if (S.equals(contractDetail.getAuditType(), "update") && (S.equals(contractDetail.getBusinessModel(), "CNC") || S.equals(contractDetail.getBusinessModel(), "SCC"))) {
            List<ProductDiffVO> productDiff = contractAuditService.getCncProductDiff(contractDetail.getId());
            contractDetail.setCncProductDiff(productDiff);
            List<ChangeMrlVO> diff = contractAuditService.getCncDiff(contractDetail.getId());
            List<ChangeMrlVO> addMrlList = diff.stream().filter(ChangeMrlVO::getIsAdd).toList();
            List<ChangeMrlVO> delMrlList = diff.stream().filter(e -> !e.getIsAdd()).toList();
            contractDetail.setAddMrl(addMrlList);
            contractDetail.setDelMrl(delMrlList);
        }
        if (S.equals(contractDetail.getAuditType(), "update")) {
            List<ProductDiffVO> productDiff = contractAuditService.getProductDiff(contractDetail.getId());
            List<DiffVO> diff = contractAuditService.getDiff(contractDetail.getId());
            Map<String, String> productMap = new HashMap<>();
            for (ProductDiffVO productDiffVO : productDiff) {
                processField(productMap, "beforeCount", productDiffVO.getBeforeCount(), productDiffVO, true);
                processField(productMap, "afterCount", productDiffVO.getAfterCount(), productDiffVO, false);
                processField(productMap, "beforePrice", productDiffVO.getBeforePrice(), productDiffVO, true);
                processField(productMap, "afterPrice", productDiffVO.getAfterPrice(), productDiffVO, false);
                processField(productMap, "beforeAmount", productDiffVO.getBeforeAmount(), productDiffVO, true);
                processField(productMap, "afterAmount", productDiffVO.getAfterAmount(), productDiffVO, false);
                processField(productMap, "beforeDiscount", productDiffVO.getBeforeDiscount(), productDiffVO, true);
                processField(productMap, "afterDiscount", productDiffVO.getAfterDiscount(), productDiffVO, false);
            }
            contractDetail.setProductDiffVO(productMap);
            contractDetail.setDiffVOList(diff);
        }
        List<ContractProductListVO> contractProductVOList = contractDetail.getContractProductVOList();
        if (contractProductVOList != null && !contractProductVOList.isEmpty()) {
            contractDetail.setDiscounts(contractProductVOList.stream().map(e -> Optional.ofNullable(e.getProductDiscount()).orElse(BigDecimal.ONE).toString()).collect(Collectors.joining("/")));
            contractDetail.setProductTypes(contractProductVOList.stream().map(ContractProduct::getProductType).collect(Collectors.joining("/")));
        }
    }

    private void processField(Map<String, String> productMap, String fieldKey, Object flagValue, ProductDiffVO vo, Boolean b) {
        if (b && flagValue == null) {
            return;
        }
        String prefix = "";
        String valueKey = fieldKey;
        if (!b && flagValue == null) {
            valueKey = valueKey.replace("after", "before");
            prefix = "删除：";
        }
        String proCode = vo.getProCode() != null ? vo.getProCode() : "";
        String model = vo.getProductModel() != null ? vo.getProductModel() : "";
        Object fieldValue = getFieldValue(valueKey, vo);
        if (fieldValue == null) {
            return;
        }
        String valuePart = new StringBuilder()
                .append(prefix)
                .append(proCode).append(" ")
                .append(model).append(":")
                .append(toStr(fieldValue, ""))
                .toString();

        String existing = productMap.get(fieldKey);
        String newValue = (existing != null) ? existing + ";" + valuePart : valuePart;
        productMap.put(fieldKey, newValue);
    }

    private Object getFieldValue(String fieldKey, ProductDiffVO vo) {
        return switch (fieldKey) {
            case "beforeCount" -> vo.getBeforeCount();
            case "afterCount" -> vo.getAfterCount();
            case "beforePrice" -> vo.getBeforePrice();
            case "afterPrice" -> vo.getAfterPrice();
            case "beforeAmount" -> vo.getBeforeAmount();
            case "afterAmount" -> vo.getAfterAmount();
            case "beforeDiscount" -> vo.getBeforeDiscount();
            case "afterDiscount" -> vo.getAfterDiscount();
            default -> throw new IllegalArgumentException("无效字段");
        };
    }

    private String toStr(Object beforeCount, String s) {
        if (beforeCount == null) {
            return s;
        }
        return String.valueOf(beforeCount);
    }

    @InterfaceLog(code = "OA_CONTRACT_SEND", name = "合同推送", direction = "CRM -> OA")
    public String accessOaInterface(String url, MultiValueMap<String, Object> params) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        headers.setBasicAuth(oaAuthUsername, oaAuthPassword);
        HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(params, headers);
        ResponseEntity<String> exchange = restTemplate.exchange(oaUrl + url, HttpMethod.POST, entity, String.class);
        return exchange.getBody();
    }


    /**
     * OA推送请求封装
     *
     * @param contractDetail
     * @return
     * @author 陈广煜
     * @date 2024-09-03
     */
    private MultiValueMap<String, Object> getOaReqVO(ContractAuditVO contractDetail) throws Exception {
        contractDetail.setAuditType(contractDetail.getAuditType());
        String oaTemplateId = getOaTemplateId(contractDetail);
        OaReqVO oaReqVO = new OaReqVO();
        String title = contractDetail.getContractCode() + contractDetail.getBusinessModelLabel().getLabel() + contractDetail.getAuditTypeLabel().getLabel();
        oaReqVO.setDocSubject(title);
        oaReqVO.setFdTemplateId(oaTemplateId);
        JSONObject json = new JSONObject();
        // OA发起人调整为合同创建人
        IamUser user = iamUserService.getEntity(contractDetail.getCreateBy());
        json.set("PersonNo", user.getUserNum());
        oaReqVO.setDocCreator(json.toJSONString(1));
        String code = "sys-" + contractDetail.getBusinessModel() + "-" + contractDetail.getAuditType();
//        if (S.equals(contractDetail.getAuditType(), "cancel")) {
//            code = "cancel";
//        }
        contractDetail.setAuditType("sys-" + contractDetail.getAuditType());
        oaReqVO.setFormValues(fieldMappingService.entityToJson(contractDetail, code));
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        MultiValueMap<String, Object> stringObjectMultiValueMap = fileBuild(contractDetail);
        if (stringObjectMultiValueMap != null) {
            map.addAll(stringObjectMultiValueMap);
        }
        map.add("docSubject", oaReqVO.getDocSubject());
        map.add("fdTemplateId", oaReqVO.getFdTemplateId());
        map.add("docContent", oaReqVO.getDocContent());
        map.add("formValues", oaReqVO.getFormValues());
        map.add("docStatus", oaReqVO.getDocStatus());
        map.add("docCreator", oaReqVO.getDocCreator());
        map.add("fdKeyword", oaReqVO.getFdKeyword());
        map.add("docProperty", oaReqVO.getDocProperty());
        map.add("flowParam", oaReqVO.getFlowParam());
        map.add("identity", oaReqVO.getIdentity());
        return map;
    }

    /**
     * 获取OA模板ID
     *
     * @return
     * @author 陈广煜
     * @date 2024-09-09
     */
    public String getOaTemplateId(ContractAuditVO contractDetail) {
        QueryWrapper<OaTemplate> queryWrapper = new QueryWrapper<>();
        String code = contractDetail.getBusinessModel() + "-" + contractDetail.getAuditType();
//        if (S.equals("cancel", contractDetail.getAuditType())) {
//            code = "cancel";
//        }
        queryWrapper.lambda().eq(OaTemplate::getCode, code);
        OaTemplate singleEntity = oaTemplateService.getSingleEntity(queryWrapper);
        return singleEntity.getTemplateId();
    }

    public String getOaTemplateId(String code, String type) {
        QueryWrapper<OaTemplate> queryWrapper = new QueryWrapper<>();
        code = code + "-" + type;
//        if (S.equals("cancel", type)) {
//            code = "cancel";
//        }
        queryWrapper.lambda().eq(OaTemplate::getCode, code);
        OaTemplate singleEntity = oaTemplateService.getSingleEntity(queryWrapper);
        if (singleEntity == null) {
            throw new RuntimeException("未找到对应的OA模板[" + code + "]");
        }
        return singleEntity.getTemplateId();
    }

    public MultiValueMap<String, Object> buildOaParam(ProjectContractAuditDetailVO contract) throws Exception {
        String auditType = contract.getAuditType();
        String oaTemplateId = getOaTemplateId("proj-" + contract.getBusinessModel(), auditType);
        String code = "proj-" + contract.getBusinessModel() + "-" + auditType;
//        if (S.equals(contract.getAuditType(), "cancel")) {
//            code = "cancel";
//        }
        return buildOaParam(contract, oaTemplateId, code);
    }

    public MultiValueMap<String, Object> buildOaParam(ProjectContractAuditDetailVO contract, String templateId, String code) throws Exception {
        OaReqVO oaReqVO = new OaReqVO();
        String title = contract.getContractCode() + contract.getBusinessModelLabel().getLabel() + contract.getAuditTypeLabel().getLabel();
        oaReqVO.setDocSubject(title);
        oaReqVO.setFdTemplateId(templateId);
        JSONObject json = new JSONObject();
        IamUser user = iamUserService.getEntity(contract.getCreateBy());
        json.set("PersonNo", user.getUserNum());
        oaReqVO.setDocCreator(json.toJSONString(1));
        oaReqVO.setFormValues(fieldMappingService.entityToJson(contract, code));
        String contractFile = contract.getProjectSamContract().getContractSupplement();
        String attachmentFile = contract.getProjectSamContract().getAppendix();
        String auditAttachment = "";
        String fileKey = "fd_fdAttachment";
        if (S.equals(contract.getBusinessModel(), "IS")) {
            fileKey = "fd_hetfj_file";
        }
        if (S.contains(contract.getAuditType(), ContractAuditTypeEnum.CANCEL.getCode())) {
            fileKey = "fd_fdAttachment";
            auditAttachment = contract.getProjectContractCancel().getAuditAttachment();
        }
        List<String> fIds = new ArrayList<>();
        if (!S.contains(contract.getAuditType(), ContractAuditTypeEnum.CANCEL.getCode())) {
            if (S.isNotBlank(contractFile)) {
                List<String> strings = S.splitToList(contractFile);
                fIds.addAll(strings);
            }
            if (S.isNotBlank(attachmentFile)) {
                fIds.addAll(S.splitToList(attachmentFile));
            }
        }
        if (S.isNotBlank(auditAttachment)) {
            fIds.addAll(S.splitToList(auditAttachment));
        }
        List<FileRecord> files = new ArrayList<>();
        if (!fIds.isEmpty()) {
            files = fileRecordService.getEntityListByIds(fIds);
        }
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        for (int i = 0; i < files.size(); i++) {
            map.add("attachmentForms[" + i + "].fdKey", fileKey);
            map.add("attachmentForms[" + i + "].fdFileName", files.get(i).getFileName());
            InputStream is = fileStorageService.getFile(files.get(i).getStoragePath());
            String systemTempDir = FileHelper.getSystemTempDir();
            if (!systemTempDir.endsWith("/")) {
                systemTempDir += "/";
            }
            String tmpFilePath = systemTempDir + BaseConfig.getProperty("spring.application.name", "diboot")
                    + com.diboot.file.config.Cons.FILE_PATH_SEPARATOR + S.newUuid() + FileHelper.getFileExtByName(files.get(i).getFileName());
            File file = new File(tmpFilePath);
            FileUtil.writeFromStream(is, file);
            map.add("attachmentForms[" + i + "].fdAttachment", new FileSystemResource(file));
        }
        map.add("docSubject", oaReqVO.getDocSubject());
        map.add("fdTemplateId", oaReqVO.getFdTemplateId());
        map.add("docContent", oaReqVO.getDocContent());
        map.add("formValues", oaReqVO.getFormValues());
        map.add("docStatus", oaReqVO.getDocStatus());
        map.add("docCreator", oaReqVO.getDocCreator());
        map.add("fdKeyword", oaReqVO.getFdKeyword());
        map.add("docProperty", oaReqVO.getDocProperty());
        map.add("flowParam", oaReqVO.getFlowParam());
        map.add("identity", oaReqVO.getIdentity());
        return map;
    }


    /**
     * 根据合同详情构建文件映射
     *
     * @param contractDetail 合同审计视图对象，包含合同详细信息
     * @throws Exception 如果文件构建过程中发生错误，则抛出异常
     */
    public MultiValueMap<String, Object> fileBuild(ContractAuditVO contractDetail) throws Exception {
        // 审批类型为取消
        if (S.contains(contractDetail.getAuditType(), ContractAuditTypeEnum.CANCEL.getCode())) {
//            String auditAttachment = contractDetail.getAuditAttachment();
//            String attachmentFile = "";
//            if (contractDetail.getContractInfoVO() != null) {
//                attachmentFile = contractDetail.getContractInfoVO().getAttachmentFile();
//            }
            String cancelFile = contractDetail.getContractAuditCancelVO().getAuditAttachment();
            List<String> fIds = new ArrayList<>();
            if (S.isNotBlank(cancelFile)) {
                fIds.addAll(S.splitToList(cancelFile));
            }
//            if (S.isNotBlank(auditAttachment)) {
//                fIds.addAll(S.splitToList(auditAttachment));
//            }
//            if (S.isNotBlank(attachmentFile)) {
//                fIds.addAll(S.splitToList(attachmentFile));
//            }
            return fileMapBuild(fIds, "fd_fdAttachment", 0);
        }
        // 数控事业部&专项指挥部
        if (S.equals("CNC", contractDetail.getBusinessModel()) || S.equals("SCC", contractDetail.getBusinessModel())) {
            // 获取合同文件和附件文件
            List<String> contractFileFiles = S.splitToList(contractDetail.getContractInfoVO().getContractFile());
            List<String> attachmentFileFiles = S.splitToList(contractDetail.getContractInfoVO().getAttachmentFile());
            // 合并文件列表
            List<String> files = new ArrayList<>();
            if (contractFileFiles != null && !contractFileFiles.isEmpty()) {
                files.addAll(contractFileFiles);
            }
            if (attachmentFileFiles != null && !attachmentFileFiles.isEmpty()) {
                files.addAll(attachmentFileFiles);
            }
            // 构建文件映射
            return fileMapBuild(files, "fd_fdAttachment", 0);
        }
        // 红外事业部
        if (S.equals("IPL", contractDetail.getBusinessModel())) {
            // 构建技术要求附件和合同文件的映射
            MultiValueMap<String, Object> fdJishuyqfjFile = fileMapBuild(S.splitToList(contractDetail.getTechnicalRequirementsAttachment()), "fd_jishuyqfj_file", 0);
            String attachmentFile = contractDetail.getContractInfoVO().getAttachmentFile();
            List<String> attachmentFileFiles = new ArrayList<>(S.splitToList(attachmentFile));
            attachmentFileFiles.addAll(S.splitToList(contractDetail.getContractInfoVO().getContractFile()) == null ? new ArrayList<>() : S.splitToList(contractDetail.getContractInfoVO().getContractFile()));
            MultiValueMap<String, Object> fdHetfjFile = fileMapBuild(attachmentFileFiles, "fd_hetfj_file", attachmentFileFiles.size());
            return merge(fdJishuyqfjFile, fdHetfjFile);
        }
        // 国际事业部
        if (S.equals("IS", contractDetail.getBusinessModel())) {
            // 构建合同文件映射
            return fileMapBuild(S.splitToList(contractDetail.getContractInfoVO().getContractFile()), "fd_hetfj_file", 0);
        }
        return null;
    }

    private MultiValueMap<String, Object> merge(MultiValueMap<String, Object> map1, MultiValueMap<String, Object> map2) {
        if (map1 == null) {
            return map2;
        }
        if (map2 == null) {
            return map1;
        }
        map1.addAll(map2);
        return map1;
    }

    /**
     * 构建文件映射
     * 根据文件ID列表获取文件记录，并将文件信息添加到指定的MultiValueMap中
     * 如果fIds为空，则不执行任何操作
     *
     * @param fileIds   文件id列表
     * @param fieldName 文件字段名，用于在map中关联文件信息
     * @throws Exception 如果文件处理过程中发生错误，则抛出异常
     */
    private MultiValueMap<String, Object> fileMapBuild(List<String> fileIds, String fieldName, int num) throws Exception {
        if (fileIds == null || fileIds.isEmpty()) {
            return null;
        }
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        List<FileRecord> files = fileRecordService.getEntityListByIds(fileIds);
        // 遍历文件记录列表，将文件信息添加到map中
        for (int i = 0; i < files.size(); i++) {
            // 添加文件的字段名
            map.add("attachmentForms[" + (i + num) + "].fdKey", fieldName);
            // 添加文件名
            map.add("attachmentForms[" + (i + num) + "].fdFileName", files.get(i).getFileName());
            // 获取文件流
            InputStream is = fileStorageService.getFile(files.get(i).getStoragePath());
            // 获取系统临时目录路径
            String systemTempDir = FileHelper.getSystemTempDir();
            // 确保临时目录路径以斜杠结尾
            if (!systemTempDir.endsWith("/")) {
                systemTempDir += "/";
            }
            // 构建临时文件路径
            String tmpFilePath = systemTempDir + BaseConfig.getProperty("spring.application.name", "diboot")
                    + com.diboot.file.config.Cons.FILE_PATH_SEPARATOR + S.newUuid() + "." + FileHelper.getFileExtByName(files.get(i).getFileName());
            // 创建临时文件
            File file = new File(tmpFilePath);
            // 将文件流写入临时文件
            FileUtil.writeFromStream(is, file);
            // 将临时文件作为资源添加到map中
            map.add("attachmentForms[" + (i + num) + "].fdAttachment", new FileSystemResource(file));
        }
        return map;
    }


}
